#!/usr/bin/env python3
"""
优化的目录扫描器
使用缓存、并行扫描和批量IO操作提升性能
"""

import os
import time
import hashlib
import pickle
from typing import List, Optional, Set, Tuple, Iterator
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed
from functools import lru_cache
from dataclasses import dataclass
from models.git_repository import GitRepository


@dataclass
class ScanResult:
    """扫描结果数据类"""
    path: str
    is_git_repo: bool
    depth: int
    scan_time: float


class OptimizedDirectoryScanner:
    """优化的目录扫描器"""
    
    def __init__(self, 
                 max_depth: int = 10,
                 cache_dir: str = "/tmp/git_pull_cache",
                 cache_ttl: int = 3600,
                 batch_size: int = 100,
                 max_workers: int = 4):
        """
        初始化优化的目录扫描器
        
        Args:
            max_depth: 最大扫描深度
            cache_dir: 缓存目录
            cache_ttl: 缓存有效期（秒）
            batch_size: 批处理大小
            max_workers: 最大工作线程数
        """
        self.max_depth = max_depth
        self.cache_dir = cache_dir
        self.cache_ttl = cache_ttl
        self.batch_size = batch_size
        self.max_workers = max_workers
        
        # 创建缓存目录
        os.makedirs(cache_dir, exist_ok=True)
        
        # 性能统计
        self.stats = {
            'total_directories': 0,
            'git_repos_found': 0,
            'cache_hits': 0,
            'scan_time': 0.0
        }
    
    def find_git_repositories(self, 
                           root_directory: str, 
                           include_patterns: Optional[List[str]] = None,
                           exclude_patterns: Optional[List[str]] = None) -> List[str]:
        """
        查找所有 Git 仓库（优化版本）
        
        Args:
            root_directory: 根目录路径
            include_patterns: 包含模式列表
            exclude_patterns: 排除模式列表
            
        Returns:
            List[str]: Git 仓库路径列表
        """
        start_time = time.time()
        git_repos = []
        
        if not os.path.exists(root_directory) or not os.path.isdir(root_directory):
            return git_repos
        
        try:
            # 获取目录结构（并行扫描）
            directories = self._scan_directories_parallel(root_directory)
            
            # 批量检查 Git 仓库
            git_repos = self._batch_check_git_repos(directories)
            
            # 应用过滤模式
            git_repos = self._apply_filters(git_repos, include_patterns, exclude_patterns)
            
        except Exception as e:
            print(f"扫描目录时发生错误: {str(e)}")
        
        # 更新统计信息
        self.stats['scan_time'] = time.time() - start_time
        self.stats['git_repos_found'] = len(git_repos)
        
        return git_repos
    
    def _scan_directories_parallel(self, root_directory: str) -> List[Tuple[str, int]]:
        """
        并行扫描目录结构
        
        Args:
            root_directory: 根目录路径
            
        Returns:
            List[Tuple[str, int]]: (目录路径, 深度) 列表
        """
        directories = []
        root_path = Path(root_directory)
        
        def scan_worker(current_path: Path, current_depth: int) -> List[Tuple[str, int]]:
            """工作线程：扫描子目录"""
            if current_depth > self.max_depth:
                return []
            
            results = [(str(current_path), current_depth)]
            
            try:
                # 并行处理子目录
                with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                    futures = []
                    for child in current_path.iterdir():
                        if child.is_dir() and not child.name.startswith('.'):
                            futures.append(
                                executor.submit(scan_worker, child, current_depth + 1)
                            )
                    
                    for future in as_completed(futures):
                        results.extend(future.result())
                        
            except PermissionError:
                pass  # 忽略权限错误
            except Exception as e:
                print(f"扫描 {current_path} 时出错: {e}")
            
            return results
        
        # 从根目录开始扫描
        directories = scan_worker(root_path, 0)
        self.stats['total_directories'] = len(directories)
        
        return directories
    
    def _batch_check_git_repos(self, directories: List[Tuple[str, int]]) -> List[str]:
        """
        批量检查 Git 仓库
        
        Args:
            directories: 目录列表
            
        Returns:
            List[str]: Git 仓库路径列表
        """
        git_repos = []
        
        # 分批处理
        for i in range(0, len(directories), self.batch_size):
            batch = directories[i:i + self.batch_size]
            
            # 并行检查当前批次
            with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
                future_to_dir = {
                    executor.submit(self._check_git_repo_cached, dir_path, depth): (dir_path, depth)
                    for dir_path, depth in batch
                }
                
                for future in as_completed(future_to_dir):
                    dir_path, depth = future_to_dir[future]
                    try:
                        if future.result():
                            git_repos.append(dir_path)
                    except Exception as e:
                        print(f"检查 {dir_path} 时出错: {e}")
        
        return git_repos
    
    @lru_cache(maxsize=10000)
    def _check_git_repo_cached(self, dir_path: str, depth: int) -> bool:
        """
        带缓存的 Git 仓库检查
        
        Args:
            dir_path: 目录路径
            depth: 目录深度
            
        Returns:
            bool: 是否为 Git 仓库
        """
        # 检查缓存
        cache_key = self._get_cache_key(dir_path)
        cached_result = self._get_from_cache(cache_key)
        
        if cached_result is not None:
            self.stats['cache_hits'] += 1
            return cached_result
        
        # 实际检查
        is_git_repo = self._is_git_repo_optimized(dir_path)
        
        # 保存到缓存
        self._save_to_cache(cache_key, is_git_repo)
        
        return is_git_repo
    
    def _is_git_repo_optimized(self, path: str) -> bool:
        """
        优化的 Git 仓库检查
        
        Args:
            path: 路径
            
        Returns:
            bool: 是否为 Git 仓库
        """
        try:
            # 使用 Path 对象提高性能
            git_dir = Path(path) / '.git'
            return git_dir.exists() and git_dir.is_dir()
        except Exception:
            return False
    
    def _get_cache_key(self, path: str) -> str:
        """生成缓存键"""
        # 使用路径和修改时间生成唯一键
        try:
            stat = os.stat(path)
            content = f"{path}:{stat.st_mtime}:{stat.st_size}"
            return hashlib.md5(content.encode()).hexdigest()
        except:
            return hashlib.md5(path.encode()).hexdigest()
    
    def _get_from_cache(self, cache_key: str) -> Optional[bool]:
        """从缓存获取结果"""
        cache_file = os.path.join(self.cache_dir, f"{cache_key}.cache")
        
        try:
            if os.path.exists(cache_file):
                with open(cache_file, 'rb') as f:
                    data = pickle.load(f)
                    # 检查是否过期
                    if time.time() - data['timestamp'] < self.cache_ttl:
                        return data['result']
                    else:
                        # 删除过期缓存
                        os.remove(cache_file)
        except:
            pass
        
        return None
    
    def _save_to_cache(self, cache_key: str, result: bool):
        """保存结果到缓存"""
        cache_file = os.path.join(self.cache_dir, f"{cache_key}.cache")
        
        try:
            with open(cache_file, 'wb') as f:
                pickle.dump({
                    'result': result,
                    'timestamp': time.time()
                }, f)
        except:
            pass
    
    def _apply_filters(self, 
                      git_repos: List[str], 
                      include_patterns: Optional[List[str]], 
                      exclude_patterns: Optional[List[str]]) -> List[str]:
        """
        应用过滤模式
        
        Args:
            git_repos: Git 仓库列表
            include_patterns: 包含模式
            exclude_patterns: 排除模式
            
        Returns:
            List[str]: 过滤后的仓库列表
        """
        filtered_repos = git_repos
        
        # 应用排除模式
        if exclude_patterns:
            filtered_repos = [
                repo for repo in filtered_repos
                if not any(pattern in repo for pattern in exclude_patterns)
            ]
        
        # 应用包含模式
        if include_patterns:
            filtered_repos = [
                repo for repo in filtered_repos
                if any(pattern in repo for pattern in include_patterns)
            ]
        
        return filtered_repos
    
    def is_valid_git_repo(self, path: str) -> bool:
        """
        验证 Git 仓库的有效性
        
        Args:
            path: 仓库路径
            
        Returns:
            bool: 是否为有效的 Git 仓库
        """
        try:
            # 创建 GitRepository 对象进行验证
            repo = GitRepository(path)
            return repo.is_valid
        except Exception:
            return False
    
    def get_stats(self) -> dict:
        """获取性能统计信息"""
        return self.stats.copy()
    
    def clear_cache(self):
        """清理缓存"""
        try:
            for file in os.listdir(self.cache_dir):
                if file.endswith('.cache'):
                    os.remove(os.path.join(self.cache_dir, file))
        except:
            pass


def test_optimized_scanner():
    """测试优化的目录扫描器"""
    print("测试优化的目录扫描器...")
    
    scanner = OptimizedDirectoryScanner(
        max_depth=5,
        cache_dir="/tmp/git_pull_cache_test",
        batch_size=50,
        max_workers=4
    )
    
    # 扫描当前目录
    print("开始扫描...")
    start_time = time.time()
    git_repos = scanner.find_git_repositories(".", ["*"], ["*test*", "*temp*"])
    scan_time = time.time() - start_time
    
    print(f"扫描完成！用时: {scan_time:.2f}秒")
    print(f"找到 {len(git_repos)} 个 Git 仓库:")
    for repo in git_repos:
        print(f"  - {repo}")
    
    # 打印统计信息
    stats = scanner.get_stats()
    print("\n性能统计:")
    for key, value in stats.items():
        print(f"  {key}: {value}")
    
    # 测试缓存效果
    print("\n测试缓存效果...")
    start_time = time.time()
    git_repos_cached = scanner.find_git_repositories(".", ["*"], ["*test*", "*temp*"])
    cached_time = time.time() - start_time
    
    print(f"缓存扫描用时: {cached_time:.2f}秒")
    print(f"缓存加速比: {scan_time / cached_time:.2f}x")
    
    # 清理测试缓存
    scanner.clear_cache()


if __name__ == "__main__":
    test_optimized_scanner()